﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;

class Brain
{
    private readonly Dictionary<string, List<string>> _subTopics;
    private readonly Dictionary<string, List<string>> _topics;
    private readonly Dictionary<string, CoreTopics> _reveseMapping;
    private readonly Dictionary<CoreTopics, List<string>> _coreBrain;
    private readonly List<string> _randomTopics;

    private readonly Random _rand;
    private double IQ;
    private int _totallinesloaded;

    public enum CoreTopics
    {
        Positive,
        Negative,
        NotUnderstand,
        WhoAreYou,
        SayingBye,
        Goodbye,
        UnknownTopic,
        AlreadySaid,
        NewTopicPlease,
        Continue,
        InvalidTopic,
        Greet,
        TopicStart
    }
    
    public Brain()
    {
        _subTopics = new Dictionary<string, List<string>>();
        _randomTopics = new List<string>();
        _reveseMapping = new Dictionary<string, CoreTopics>();
        _coreBrain = new Dictionary<CoreTopics, List<string>>();
        _topics = new Dictionary<string, List<string>>();
        _rand = new Random();
    }


    public void Clear()
    {
        _subTopics.Clear();
        _randomTopics.Clear();
        _reveseMapping.Clear();
        _topics.Clear();
        _coreBrain.Clear();
        IQ = 0;
    }

    public void PrintAllTopics()
    {
        foreach (KeyValuePair<string, List<string>> pair in _topics)
        {
            Log.Write("\nNEW TOPIC: " + pair.Key, Log.LogLevel.Warning);
            for (int i = 0; i < pair.Value.Count; i++)
            {
                Log.Write("Line " + i + ": " + pair.Value[i], Log.LogLevel.Normal);
            }
        }
    }

    //----------------------------------------------------------------------
    //This function loads a core brain function into memory from brain.dat
    public bool LoadBrainCore(string dataFile)
    {
        if (!File.Exists(dataFile)) return false;

        //Decrypt and read file
        using (
            StreamReader readFile =
                new StreamReader(new GZipStream(new FileStream(dataFile, FileMode.Open), CompressionMode.Decompress,
                                                false)))
        {
            //Parse all lines
            CoreTopics topic = CoreTopics.InvalidTopic;
            for (string line = readFile.ReadLine(); line != null; line = readFile.ReadLine())
            {
                //Skip empty lines
                if (line.Length == 0) continue;

                switch (line[0])
                {
                    //Skip comments
                    case '#':
                        continue;

                    //New topic
                    case '@':
                        topic = (CoreTopics) Enum.Parse(typeof(CoreTopics), line.TrimStart('@'), true);

                        _coreBrain[topic] = new List<string>();
                        continue;

                    //New knowledge in topic
                    default:
                        if (topic == CoreTopics.InvalidTopic) continue;

                        _coreBrain[topic].Add(line);

                        if (_reveseMapping.ContainsKey(line))
                        {
                            Log.Write("Conflicting core topic could cause problems: " + line, Log.LogLevel.Warning);
                        }
                        else
                        {
                            _reveseMapping[line] = topic;
                        }

                        _totallinesloaded++;
                        continue;
                }
            }

            //Calculate our IQ based on our knowledge base
            IQ += Convert.ToDouble(_totallinesloaded) / 50.0;
        }

        return true;
    }

    //This function makes KID say a random line in the static brain array
    public string GetCoreLine(CoreTopics whichtopic)
    {
        List<string> topic = _coreBrain[whichtopic];
        return topic[_rand.Next(topic.Count)];
    }

    //----------------------------------------------------------------------
    //Loads all discussable topics and their answers
    public bool LoadAllTopics(string topicfile)
    {
        //Couple of error checks
        if (!DataFile.UnpackData(topicfile))
        {
            Log.Write("File does not exist: " + topicfile, Log.LogLevel.Error);
            return false;
        }

                //Decrypt and read file
        using (
            StreamReader readFile =
                new StreamReader(new GZipStream(new FileStream(topicfile, FileMode.Open), CompressionMode.Decompress, false)))
        {
            string topic = "RANDOM_TOPIC";
            string subTopic = null;

            while (!readFile.EndOfStream)
            {
                string line = readFile.ReadLine();

                if(String.IsNullOrEmpty(line)) continue;

                switch (line[0])
                {
                    //Skip comments
                    case '#':
                        continue;

                    //New topic
                    case '@':
                        line = line.Remove(0, 0);     //Skip the @ symbol

                        //Do we add it to the random topic list?
                        bool isRandom = false;
                        if (readFile.Peek() == '+')
                        {
                            isRandom = true;
                            line = line.Remove(0, 0);        //Don't load the + symbol into topic
                        }
                        
                        //Now load the actual topic name
                        topic = line;
                        _topics[topic] = new List<string>();

                        if(isRandom) _randomTopics.Add(topic);
                        break;

                    //Subtopic
                    case '$':
                        line = line.Remove(0, 0);   //Skip $ symbol
                        if (subTopic == null)
                        {
                            subTopic = line;
                            _subTopics[subTopic] = new List<string>();
                        }
                        else
                        {
                            subTopic = null;
                        }
                        break;

                    //Topic data
                    default:
                        if (subTopic == null)
                        {
                            _topics[topic].Add(line);
                        }
                        else
                        {
                           _subTopics[subTopic].Add(line); 
                        }
                        break;
                }
            }
        }

        //Add some IQ
        IQ += _topics.Count / 25.0;
        IQ += _subTopics.Count / 25.0;

        return true;
    }

    public string GetTopicRamble(string topic)
    {
        List<string> lines = _topics[topic];
        return lines[_rand.Next(lines.Count)];
    }

    public int GetTopicsLoaded()
    {
        return _topics.Count + _subTopics.Count;
    }

    //----------------------------------------------------------------------
    //This function checks if we know the specified topic
    public bool CheckTopic(CoreTopics topic, string word)
    {
        return _coreBrain[topic].Contains(word.ToUpper());
    }

    public Dictionary<CoreTopics, List<string>> GetBigBrain()
    {
        return _coreBrain;
    }

    public double GetIQ()
    {
        return IQ;
    }

    public string GetRandomTopic(ShortTermMemory memory)
    {
        string topic;
        do
        {
            topic = _randomTopics[_rand.Next(_randomTopics.Count)];
        } while (memory.IsPreviousTopic(topic));

        return topic;
    }
}
